package zheezes.socket.impl.gbw;

import java.util.Calendar;

import org.apache.log4j.Logger;

import zheezes.protocol.Frame;
import zheezes.protocol.impl.gbw.DataUnit;
import zheezes.protocol.impl.gbw.GbwFrame;
import zheezes.protocol.impl.gbw.GbwMasterAddr;
import zheezes.protocol.impl.gbw.afn02.Afn02DownF1DataUnit;
import zheezes.protocol.impl.gbw.afn02.Afn02DownF2DataUnit;
import zheezes.protocol.impl.gbw.afn02.Afn02DownF3DataUnit;
import zheezes.socket.Postman;
import zheezes.socket.Transput;
import zheezes.util.RandomUtil;


public class GbwPostman extends Postman {
	private static Logger logger = Logger.getLogger(GbwPostman.class);

	private FrameSequence[] seqs;
	private GbwMasterAddr msa;

	public GbwPostman(GbwMasterAddr msa) {
		this.msa = msa;
		initSequence();
	}

	@Override
	public int login() {
		logger.info(String.format("%s login", desc()));
		GbwFrame frame = new GbwFrame();
		// GbwFrame rfrm = null;
		Frame[] frames = null;
		byte afn = 0x02;
		frame.setMaster(msa);
		frame.setAfn(afn);
		DataUnit ldu = new Afn02DownF1DataUnit();
		frame.addDataUnit(ldu);
		if(sendFrame(frame) == 0 && (frames = recvFrame()) != null) {
			for(Frame f: frames) {
				if(frame.match(f)) {
					GbwFrame lf = (GbwFrame) f;
					if(lf.getAfn() == 0x00) {
						DataUnit cdu = lf.getDataUnits()[0];
						if (cdu.getId().getFn() == (short) 0x0001) {
							logger.info(String.format("%s login success", desc()));
							return 0;
						}
					}
				}
			}
		}
		return -1;
	}
	
	protected int heartbeat() {
		logger.info(String.format("%s heartbeat", desc()));
		GbwFrame frame = new GbwFrame();
		Frame[] frames = null;
		byte afn = 0x02;
		// GbwFrame rfrm = null;
		frame.setMaster(msa);
		frame.setAfn(afn);
		DataUnit ldu = new Afn02DownF3DataUnit(); /* bug fixed */
		frame.addDataUnit(ldu);
		if(sendFrame(frame) == 0 && (frames = recvFrame()) != null) {
			for(Frame f: frames) {
				if(frame.match(f)) {
					GbwFrame lf = (GbwFrame) f;
					if(lf.getAfn() == 0x00) {
						DataUnit cdu = lf.getDataUnits()[0];
						if (cdu.getId().getFn() == (short) 0x0001) {
							logger.info(String.format("%s heartbeat success", desc()));
							return 0;
						}
					}
				}
			}
		}
		return -1;
	}

	@Override
	protected int logout() {
		logger.info(String.format("%s logout", desc()));
		GbwFrame frame = new GbwFrame();
		Frame[] frames = null;
		byte afn = 0x02;
		//GbwFrame rfrm = null;
		frame.setMaster(msa);
		frame.setAfn(afn);
		DataUnit ldu = new Afn02DownF2DataUnit();
		frame.addDataUnit(ldu);
		if(sendFrame(frame) == 0 && (frames = recvFrame()) != null) {
			for(Frame f: frames) {
				if(frame.match(f)) {
					GbwFrame lf = (GbwFrame) f;
					if(lf.getAfn() == 0x00) {
						DataUnit cdu = lf.getDataUnits()[0];
						if (cdu.getId().getFn() == (short) 0x0001) {
							logger.info(String.format("%s logout success", desc()));
							return 0;
						}
					}
				}
			}
		}
		return -1;
	}
	
	protected void handle(Frame frame) {
		GbwFrame frm = (GbwFrame) frame;
		Transput transput;

		if ((transput = releaseNo(frm.getFseq())) != null) {
			// transput.setRecv(new Frame[] {frm});
			transput.addRecv(frm);
		}
	}

	@Override
	protected Transput sendFrame(Frame frame, int option) {
		/**
		 * allow the anonymous sender to send control command
		 */
		if(frame != null) {
			FrameSequence seq = acquireNo();
			if (seq != null) {
				logger.debug("acquire no = " + seq.getNo());
				seq.setOption(option);
				if (frame instanceof GbwFrame) {
					GbwFrame frm = (GbwFrame) frame;
					frm.setMaster(msa);
					frm.setFseq(seq.getNo());
					// seq.setMailbox(mailbox);
					Calendar aeadtime = Calendar.getInstance();
					aeadtime.add(Calendar.MILLISECOND, (int) frameSo);
					seq.setAeadtime(aeadtime);
					if (sendFrame(frame) == 0) {
						Transput envelop = new Transput();
						envelop.setPostman(this);
						envelop.setSend(frame);
						envelop.setTimeout(frameSo);
						return envelop;
					}
				}
			}
		}
		return null;
	}

	/* private */
	private int initSequence() {
		int no = 0x00;
		seqs = new FrameSequence[0x0F - no];
		for (int i = 0; i < seqs.length; i++) {
			seqs[i] = new FrameSequence(no++);

		}
		return seqs.length;
	}

	private FrameSequence acquireNo() {
		for (int i : RandomUtil.randUnIntArray(0, seqs.length)) {
			FrameSequence seq = seqs[i];
			if (seq.acquire() == 0) {
				return seq;
			}
		}
		return null;
	}

	private  Transput releaseNo(int no) {
		FrameSequence seq = findNo(no);
		if (seq != null) {
			seq.release();
			return seq.getMailbox();
		}
		return null;
	}

	private FrameSequence findNo(int no) {
		for (FrameSequence seq : seqs) {
			if (seq.getNo() == no) {
				return seq;
			}
		}
		return null;
	}
	
	@Override
	public String desc() {
		return String.format("%02X", msa.getBytes()[0]);
	}
}
